#ifndef _SOFT_USB_H
#define _SOFT_USB_H

#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "driver/timer.h"
#include "esp32-hal-log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "soc/timer_group_struct.h"

#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG
#define DEBUG_ALL
#endif

#if defined ESP_IDF_VERSION_MAJOR && ESP_IDF_VERSION_MAJOR >= 4
#define USE_NATIVE_GROUP_TIMERS
#else
#warning "Using software group timers"
#endif

#define TIMER_DIVIDER 2                              //  Hardware timer clock divider
#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) // convert counter value to seconds
#define TIMER_INTERVAL0_SEC (0.001)                  // sample test interval for the first timer
// non configured device -  must be zero
#define ZERO_USB_ADDRESS 0
// any number less 127, but no zero
#define ASSIGNED_USB_ADDRESS 3
// usb ports in this hub
#define NUM_USB 4

#define STDCLASS 0x00
#define HIDCLASS 0x03
#define HUBCLASS 0x09 /* bDeviceClass, bInterfaceClass */

/*获取配置描述符*/
const uint8_t SetupGetCfgDescr[] = {0x80, 0x06, 0x00, 0x02, 0x00, 0x00, 0x09, 0x00};
/*设置USB配置*/
const uint8_t SetupSetConfig[] = {0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/*设置USB接口配置*/
const uint8_t SetupSetInterface[] = {0x01, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
/*获取HID设备报表描述符*/
const uint8_t SetupHIDGetReportDescr[] = {0x81, 0x06, 0x00, 0x22, 0x00, 0x00, 0xFF, 0x00};
/*设置 SetReport */
const uint8_t SetupHIDSetReport[] = {0x21, 0x09, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00};
/*设置 HID空闲不发包 */
const uint8_t SetupHIDSetIdle[] = {0x21, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

// Device Descriptor
struct sDevDesc {
    uint8_t bLength;
    uint8_t bDescriptorType;
    uint16_t bcdUSB;
    uint8_t bDeviceClass;
    uint8_t bDeviceSubClass;
    uint8_t bDeviceProtocol;
    uint8_t bMaxPacketSize0;
    uint16_t idVendor;
    uint16_t idProduct;
    uint16_t bcdDevice;
    uint8_t iManufacturer;
    uint8_t iProduct;
    uint8_t iSerialNumber;
    uint8_t bNumConfigurations;
};

// Configuration Descriptor
struct sCfgDesc {
    uint8_t bLength;
    uint8_t bType;
    uint16_t wLength;
    uint8_t bNumIntf;
    uint8_t bCV;
    uint8_t bIndex;
    uint8_t bAttr;
    uint8_t bMaxPower;
};

// Interface Descriptor
struct sIntfDesc {
    uint8_t bLength;
    uint8_t bType;
    uint8_t iNum; // 接口号
    uint8_t iAltString;
    uint8_t bEndPoints;
    uint8_t iClass;
    uint8_t iSub;
    uint8_t iProto;
    uint8_t iIndex;
};

// HID Device Descriptor
struct HIDDescriptor {
    uint8_t bLength;
    uint8_t bDescriptorType;
    uint16_t bcdHID;
    uint8_t bCountryCode;
    uint8_t bNumDescriptors;
    uint8_t bReportDescriptorType;
    uint8_t wItemLengthL;
    uint8_t wItemLengthH;
};

// EndPoint Descriptor
struct sEPDesc {
    uint8_t bLength;
    uint8_t bType;
    uint8_t bEPAdd;
    uint8_t bAttr;
    uint16_t wPayLoad; /* low-speed this must be 0x08 */
    uint8_t bInterval;
};

struct sStrDesc {
    uint8_t bLength;
    uint8_t bType;
    uint16_t wLang;
};

class SoftUSB {
public:
    // 回调函数类型
    typedef void (*OnDetect_t)(uint8_t usbNum, sDevDesc *devDesc, size_t data_len);
    typedef void (*OnEject_t)(uint8_t usbNum);
    typedef void (*OnRecvData_t)(uint8_t usbNum, uint8_t *data, size_t data_len);

    typedef void (*OnError_t)(uint8_t usbNum, int type);
    typedef void (*OnBusResume_t)();

private:
    void initStates(int DP0, int DM0, int DP1, int DM1, int DP2, int DM2, int DP3, int DM3);
    // 由定时器每1ms运行
    // 监视引脚，建立连接(reset,set address)
    static void IRAM_ATTR timer0_isr(void *para);
    static void IRAM_ATTR TransactionPiece();
    static void IRAM_ATTR TransactionControl();
    void setCPUDelay(uint8_t ticks);

    // 回调函数
    static void (*onDetectCalback)(uint8_t usbNum, sDevDesc *devDesc, size_t data_len);
    static void (*onEjectCalback)(uint8_t usbNum);
    static void (*onRecvDataCalback)(uint8_t usbNum, uint8_t *data, size_t data_len);
    static void (*onErrorCalback)(uint8_t usbNum, int errorCode);

    static void (*onInitializedCalback)(uint8_t usbNum);
    static void (*onBusResumeCalback)();

    static void onEvent(int usbNum, int eventId, int value, uint8_t *dataBuf, int nLen);

public:
    // 初始化host
    bool init(int DP0, int DM0, int DP1, int DM1, int DP2, int DM2, int DP3, int DM3);

    // 向设备发送指令
    uint8_t *usbSetupIn(int usbNum, uint8_t *pData0, int waitForBytes, int *pResultLen);
    void usbSetupOut(int usbNum, uint8_t *pData0, uint8_t *pData1, int data1Len);

    // 调用此函数以获取数据
    void checkEvent();

    // 设置监听的端点号范围
    void setEndPoint(int usbNum, uint8_t epMin, uint8_t epMax, uint8_t interval);

    // 设置回调函数
    void setOnDetect(OnDetect_t cd);
    void setOnEject(OnEject_t cd);
    void setOnRecvData(OnRecvData_t cd);
    void setOnError(OnError_t cd);
    void setOnBusResume(OnBusResume_t cd);

    // 使用这2个函数短时间暂停软件USB的工作（超过3ms将会导致设备挂起）
    // 用以避免"Guru Meditation Error: Core 1 panic'ed (Cache disabled but cached memory region accessed)"
    // 在执行高优先级任务（如 SD 卡读写、Wi-Fi 传输）时，暂停 USB 定时器以防止中断干扰
    void TimerPause();
    void TimerResume();

    // 使用这2个函数挂起或回复软件USB总线
    void BusSuspend(); // 挂起
    void BusResume();  // 恢复

private:
    bool inited = false;
    bool paused = false;
    int intr_alloc_flags = ESP_INTR_FLAG_IRAM;
};

extern SoftUSB _SUSB;

#endif
